#ifndef esda_BinaryOperatorStream_h
#define esda_BinaryOperatorStream_h

/*
 * Copyright (C) Olivier Le Floch
 *
 * See the file LICENSE for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "common.h"
#include "Timestamp.h"
#include "Streams.h"

namespace esda
{
	class BinaryOperatorStream : public OperatorStream<double> {
	protected:
		Stream<double>& leftInputStream, rightInputStream;
		
	public:
		BinaryOperatorStream(Stream<double>& leftInStream, Stream<double>& rightInStream) : leftInputStream(leftInStream), rightInputStream(rightInStream) {};
		
		virtual Timestamp getNextStart(const Timestamp t) const;
		virtual Timestamp getNextEnd(const Timestamp t) const;
		virtual Timestamp getPreviousStart(const Timestamp t) const;
		virtual Timestamp getPreviousEnd(const Timestamp t) const;
		
		virtual Timestamp getFirstStart() const;
		virtual Timestamp getLastEnd() const;
		
		virtual double get(const Timestamp t) const;
	};
	
	class BinaryOperatorStream_Add : public BinaryOperatorStream {
	public:
		virtual double get(const Timestamp t) const;
	};
	
	class BinaryOperatorStream_Multiply : public BinaryOperatorStream {
	public:
		virtual double get(const Timestamp t) const;
	};
	
	class BinaryOperatorStream_UserFunc : public BinaryOperatorStream {
	public:
		// CAUTION ! This function *must* ''delete'' its arguments !
		typedef double (*UserFunc)(double, double);
		
		BinaryOperatorStream_UserFunc(Stream<double> &leftInStream, Stream<double> &rightInStream, UserFunc userF) : BinaryOperatorStream(leftInStream, rightInStream), userFunc(userF) {};
		
		virtual double get(const Timestamp t) const;
	
	private:
		UserFunc userFunc;
	};
	
	double BinaryOperatorStream::get(const Timestamp t) const {
		return leftInputStream.get(t);
	}
	
	Timestamp BinaryOperatorStream::getNextStart(const Timestamp t) const {
		return leftInputStream.getNextStart(t);
	}
	
	Timestamp BinaryOperatorStream::getNextEnd(const Timestamp t) const {
		return leftInputStream.getNextEnd(t);
	}
	
	Timestamp BinaryOperatorStream::getPreviousStart(const Timestamp t) const {
		return leftInputStream.getPreviousStart(t);
	}

	Timestamp BinaryOperatorStream::getPreviousEnd(const Timestamp t) const {
		return leftInputStream.getPreviousEnd(t);
	}

	Timestamp BinaryOperatorStream::getFirstStart() const {
		return leftInputStream.getFirstStart();
	}
	
	Timestamp BinaryOperatorStream::getLastEnd() const {
		return leftInputStream.getLastEnd();
	}
	
	// Ajout des valeurs de deux streams
	double BinaryOperatorStream_Add::get(const Timestamp t) const {
		return leftInputStream.get(t) + rightInputStream.get(t);
	}
	
	// Multiplication des valeurs de deux streams
	double BinaryOperatorStream_Multiply::get(const Timestamp t) const {
		return leftInputStream.get(t) * rightInputStream.get(t);
	}
	
	double BinaryOperatorStream_UserFunc::get(const Timestamp t) const {
		return userFunc(leftInputStream.get(t), rightInputStream.get(t));
	}
}

#endif
